# _*_ encoding:utf-8 _*_
# @desc: 飞书公共方法

import requests
import json
import sys
from urllib.parse import urlencode, quote
import lark_oapi as lark

from lark_oapi.api.sheets.v3 import *
from lark_oapi.api.drive.v1 import *


class Feishu():
    def __init__(self):
        # 自建应用
        self.app_key = "cli_a756a1fe763b900d"
        self.app_secret = "0sAjAbRKBj9FNUMXcdUMyd11xJVgb1BG"
        # 商店应用
        self.app_shop_key = '',
        self.app_shop_secret = '',

    def request_data(self, url, method='GET', headers=None, params=None, data=None):
        """
        公共请求方法
        :param url: 请求url
        :param method:  请求方式
        :param headers:  请求头
        :param params:  请求参数
        :param data:    请求参数
        :return:
        """
        try:
            if method == 'POST':
                response = requests.post(url, headers=headers, params=params, data=data)
            else:
                response = requests.get(url, headers=headers, params=params)
        except Exception as e:
            print("接口请求出错：", str(e))
            return None
        else:
            return response.json()

    def get_shop_tenant_access_token(self):
        """
        商店应用获取 tenant_access_token
        :return:
        """
        url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token'
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_access_token": "",  # 应用访问凭证，通过商店应用获取 app_access_token接口获取
            "tenant_key": "",
            # 租户在飞书上的唯一标识，也可以理解为企业标识可以通过如下方式获取：企业开通应用时，开放平台推送给应用，具体可参考首次启用应用用户登录到小程序、H5 应用或者浏览器应用时，在用户的身份信息中获取
        }
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_app_ticket(self):
        """重新获取 app_ticket"""
        url = "https://open.feishu.cn/open-apis/auth/v3/app_ticket/resend"
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_id": self.app_shop_key,
            "app_secret": self.app_shop_secret,
            "app_ticket": '',  # 平台定时推送给商店应用的临时凭证。该凭证的获取方式如下：
        }
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[app_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_shop_app_access_token(self):
        """
        商店应用获取 app_access_token
        """
        url = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token'
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_id": self.app_shop_key,
            "app_secret": self.app_shop_secret,
            "app_ticket": '',  # 平台定时推送给商店应用的临时凭证。该凭证的获取方式如下：
        }
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[app_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_tenant_access_token(self):
        """
        自建应用获取 tenant_access_token
        :return:
        """
        url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal'
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_id": self.app_key,
            "app_secret": self.app_secret,
        }
        # resp = requests.post(url, headers=headers, data=json.dumps(post_data))
        # resp = resp.json()
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print("tenant_access_token接口返回：", resp)
        return resp

    def get_app_access_token(self):
        """
        自建应用获取app_access_token
        """
        url = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal'
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_id": self.app_key,
            "app_secret": self.app_secret,
        }
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[app_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_user_access_token(self):
        """
        获取 user_access_token
        :return:
        """
        url = 'https://open.feishu.cn/open-apis/authen/v2/oauth/token'
        post_data = {
            "grant_type": "authorization_code",  # 授权类型。
            "client_id": self.app_key,
            "client_secret": self.app_secret,
            "code": ""  # 授权码，详见获取授权码。
        }
        headers = {"Content-Type": "application/json; charset=utf-8"}
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def flush_user_access_token(self):
        """刷新 user_access_token"""
        url = "https://open.feishu.cn/open-apis/authen/v2/oauth/token"

        post_data = {
            "grant_type": "refresh_token",  # 授权类型。
            "client_id": self.app_key,
            "client_secret": self.app_secret,
            "refresh_token": ""
            # 刷新令牌，用于刷新 user_access_token 以及 refresh_token    #接口仅支持获取 user_access_token和刷新 user_access_token接口返回的 refresh_token
        }
        headers = {"Content-Type": "application/json; charset=utf-8"}
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_authorize_code(self, redirect_uri):
        """
        获取授权码
        :param redirect_uri: 应用重定向地址，在用户授权成功后会跳转至该地址，同时会携带 code 以及 state 参数（如有传递 state 参数
        :param state: 用来维护请求和回调之间状态的附加字符串，在授权完成回调时会原样回传此参数。应用可以根据此字符串来判断上下文关系，同时该参数也可以用以防止 CSRF 攻击，请务必校验 state 参数前后是否一致。
        :return:
        """
        url = "https://accounts.feishu.cn/open-apis/authen/v1/authorize"
        post_data = {
            "client_id": self.app_key,
            "redirect_uri": quote(redirect_uri),
        }
        url = self.request_data(url, method='GET', headers={}, data=json.dumps(post_data))

    def get_jssdk_ticket(self):
        """
        获取 JSAPI 临时授权凭证
        """
        url = "https://open.feishu.cn/open-apis/jssdk/ticket/get"
        post_data = {
            "grant_type": "refresh_token",  # 授权类型。
            "client_id": self.app_key,
            "client_secret": self.app_secret,
            "refresh_token": ""
            # 刷新令牌，用于刷新 user_access_token 以及 refresh_token    #接口仅支持获取 user_access_token和刷新 user_access_token接口返回的 refresh_token
        }
        headers = {"Content-Type": "application/json; charset=utf-8",
                   "Authorization": "Bearer {}".format("tenant_access_token")
                   }
        resp = self.request_data(url, method='POST', headers=headers, data=json.dumps(post_data))
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print(resp)
        return resp

    def get_folder(self):
        """
        获取我的空间（root folder）元数据
        :return:
        """
        url = "https://open.feishu.cn/open-apis/drive/explorer/v2/root_folder/meta"

        tenant_access_token = self.get_tenant_access_token()['tenant_access_token']
        headers = {
            "Authorization": "Bearer {}".format(tenant_access_token)
        }
        print("获取文件列表url:", url)

        resp = self.request_data(url, method='GET', headers=headers)
        if resp['code'] != 0:
            print("请求接口[get_clound_files_list]失败：", resp['msg'])
        print(resp)
        return resp

    def get_clound_files_list(self):
        """获取文件夹中的文件清单"""
        url = "https://open.feishu.cn/open-apis/drive/v1/files"
        post_data = {
            "page_size": 50
        }
        tenant_access_token = self.get_tenant_access_token()['tenant_access_token']
        headers = {"Content-Type": "application/json; charset=utf-8",
                   "Authorization": "Bearer {}".format(tenant_access_token)
                   }
        print("获取文件列表url:", url)

        resp = self.request_data(url, method='GET', headers=headers, params=post_data)
        if resp['code'] != 0:
            print("请求接口[get_clound_files_list]失败：", resp['msg'])
        print(resp)
        return resp

    def get_clound_spreadsheets(self):
        """
        获取电子表格信息
        """
        url = "https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/:spreadsheet_token"
        post_data = {
            "page_size": 50
        }
        tenant_access_token = self.get_tenant_access_token()['tenant_access_token']
        headers = {"Content-Type": "application/json; charset=utf-8",
                   "Authorization": "Bearer {}".format(tenant_access_token)
                   }
        resp = self.request_data(url, method='GET', headers=headers)
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print(resp)
        return resp


class FeishuSdk():
    def __init__(self):
        # 自建应用
        self.app_key = "cli_a756a1fe763b900d"
        self.app_secret = "0sAjAbRKBj9FNUMXcdUMyd11xJVgb1BG"
        # 商店应用
        self.app_shop_key = '',
        self.app_shop_secret = '',
        # 创建client
        self.client = lark.Client.builder() \
            .app_id(self.app_key) \
            .app_secret(self.app_secret) \
            .log_level(lark.LogLevel.DEBUG) \
            .build()

    def get_tenant_access_token(self):
        """
        自建应用获取 tenant_access_token
        :return:
        """
        url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal'
        headers = {"Content-Type": "application/json; charset=utf-8"}
        post_data = {
            "app_id": self.app_key,
            "app_secret": self.app_secret,
        }
        resp = requests.post(url, headers=headers, data=json.dumps(post_data))
        resp = resp.json()
        if resp['code'] != 0:
            print("请求接口[tenant_access_token]失败：", resp['msg'])
        print("tenant_access_token接口返回：", resp)
        return resp

    def get_folder_token(self):
        """
        获取我的空间（root folder）元数据
        :return:
        """
        url = "https://open.feishu.cn/open-apis/drive/explorer/v2/root_folder/meta"

        tenant_access_token = self.get_tenant_access_token()['tenant_access_token']
        headers = {
            "Authorization": "Bearer {}".format(tenant_access_token)
        }
        print("获取root_folder元数据url:", url)

        resp = requests.get(url, headers=headers)
        resp = resp.json()
        if resp['code'] != 0:
            print("请求接口[get_folder_token]失败：", resp['msg'])
        return resp

    def get_folder_list(self):
        """获取文件夹中的文件清单"""

        folder_token = self.get_folder_token()['data']['token']
        print("get_folder:", folder_token)

        # 构造请求对象
        request: ListFileRequest = ListFileRequest.builder() \
            .page_size(50) \
            .folder_token(folder_token) \
            .order_by("EditedTime") \
            .direction("DESC") \
            .user_id_type("open_id") \
            .build()

        # 发起请求
        response: ListFileResponse = self.client.drive.v1.file.list(request)
        print("文件清单:",response)

        # 处理失败返回
        if not response.success():
            lark.logger.error(
                f"client.drive.v1.file.list failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}")
            return

        # 处理业务结果
        lark.logger.info(lark.JSON.marshal(response.data, indent=4))

    def create_spreadsheets(self):
        """
        创建电子表格
        """

        title = "基础测试"
        # 构造请求对象
        request: CreateSpreadsheetRequest = CreateSpreadsheetRequest.builder() \
            .request_body(Spreadsheet.builder()
                          .title(title)
                          .folder_token("Z8HXfwZmPlHlQhdf1Zgc3PNxnVb")
                          .build()) \
            .build()

        # 发起请求
        response: CreateSpreadsheetResponse = self.client.sheets.v3.spreadsheet.create(request)

        # 处理失败返回
        if not response.success():
            lark.logger.error(
                f"client.sheets.v3.spreadsheet.create failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}")
            return

        # 处理业务结果
        lark.logger.info(lark.JSON.marshal(response.data, indent=4))


def main():
    # feishu = Feishu()
    # 获取自建应用获取 tenant_access_token
    # feishu.get_tenant_access_token()
    # feishu.get_app_access_token()

    # 获取文件夹中的文件清单
    # feishu.get_folder()
    # feishu.get_clound_files_list()

    feishu_sdk = FeishuSdk()
    feishu_sdk.get_folder_list()
    # feishu_sdk.create_spreadsheets()


if __name__ == '__main__':
    main()
